package SSF.OS.NetFlow.Filter;

import com.renesys.raceway.SSF.*;
import com.renesys.raceway.DML.*;

import SSF.OS.NetFlow.*;
import SSF.Util.Streams.*;
import java.util.*;

/**
 * It's composed by one or more "Terms". Between each term there is an "OR" 
 * operation.
 * It also uses a decoderTable to store those decoders that it will use.
 * <BR>
 * Configure the filter:(Both multiple decoders and multiple terms are allowed.
 * <UL>
 * <LI> decoder -- a decoder that should be used.
 * <LI> term -- a term. The relationship among the terms are "OR".
 * </UL>
 * @see Term
 * @see Factor
 * @see Range
 */
public class Filter implements BasicFilter
{
    /** filterPlayer that uses this filter*/
    StreamInterface player;

    /** the table storing all decoders.*/
    Hashtable decoderTable;

    /** those terms compose this filter*/
    private LinkedList terms;

    public Filter(StreamInterface bPlayer, Configuration cfg) 
	throws configException
    {
	//config the decoders
	decoderTable = new Hashtable();
	player = bPlayer;

	for (Enumeration decoderConfigs=cfg.find("decoder");
	     decoderConfigs.hasMoreElements();) {
		configDecoder((Configuration)decoderConfigs.nextElement());
	}

	//config the Terms
	configTerms(cfg);
    }
    public Filter(Filter parentFilter, Configuration cfg) 
	throws configException
    {
        //inherit the decoders and the player.
	decoderTable = parentFilter.decoderTable;
	player = parentFilter.player;

	//config the Terms
	configTerms(cfg);
    }

    /**
     * each term of the filter is a "OR" phrase, so return true
     * if any of the term says this record should pass
     */
    public boolean passFilter(FilterData record)
    {
	Term t;

	for (ListIterator iter = terms.listIterator();
	     iter.hasNext();) {
	    t = (Term)iter.next();
	    if (t.passFilter(record) == true)
		return true;
	}
	return false;
    }

    /**
     * Now the recordID & recordType pair is known. Place the decoder in
     * using the ID as key
     * @param recordType the type of the record
     * @param reocrdID the corresponding recordID
     */
    public void addDecoder(String recordType, Integer recordID)
    {
	//for debug
	//System.err.println("adding decoder for:"+recordType+" "+recordID);

	// get the decoder
	Decoder obj = (Decoder)decoderTable.get(recordType);

	// put it back in using the ID as index
	if (obj != null) {
	    decoderTable.remove(recordType);
	    decoderTable.put(recordID, obj);
	    System.err.println("decoder added for "+recordType+"("+recordID+")");
	}
	else {
	    System.err.println("decoder not added because it's not in table");
	}
    }
    ////////////////// private method/////////////////////
    /**
     * config the terms
     */
    private void configTerms(Configuration cfg) throws configException
    {
	//Here the order of the terms are still kept because it will affect
	//the performance of the filter a lot.

	terms = new LinkedList();
	Term tmpTerm=null;

	for (Enumeration termConfigs = cfg.find("term");
	     termConfigs.hasMoreElements();) {
	    
	    //Configuration termcfg = (Configuration)termConfigs.nextElement();
	    tmpTerm = 
		new Term(this, (Configuration)(termConfigs.nextElement()));
	   
	    terms.addLast(tmpTerm);
	}
    }

    /**
     * config the decoders
     */
    private Decoder configDecoder(Configuration cfg) throws configException
    {
	Decoder result = null;
	
	// get record type
	String recordType = (String)cfg.findSingle("record_type");
	if (recordType == null)
	    throw new 
		configException("No record_type specified for the decoder");

	// get related record type ID
	int recordID = player.getRecordTypeCode(recordType);

	// get the corresponding class that should be used
	String className = (String)cfg.findSingle("use");
	if (className==null) {
	    System.err.println("No decoder class specified for record type "+recordType);
	    throw new configException("no decoder class specified for record type "+recordType);
	}

	// create an instance of the decoder
	try {
	    Class dclass = Class.forName(className);
	    Object dobj = dclass.newInstance();
	    result = (Decoder) dobj;
	} catch (Exception any) {
	    System.err.println("Can't use Decoder: "+ className);
	    any.printStackTrace();
	    throw new configException
		("can't use Decoder "+className);
	}

	// put it in the decoder table, using the name as index.
	decoderTable.put(recordType, result);
	System.err.println("Decoder for "+recordType+" is required");

	return result;
    }

}
